﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using SlimDX;
using SlimDX.Direct3D10;
using System.Windows.Media.Imaging;
using System.IO;

namespace EikonLeap
{
    public class ScreenPlate: SceneObject
    {
        public ScreenPlate(int _hwnd = 0)
        {
            zoom = 1.0f;
            if (_hwnd == 0)
            {
                _hwnd = 0x10010;
            }
            hwnd = (IntPtr)_hwnd;
            Vertices = new Vector3[] { 
                new Vector3(-0.5f, -0.5f, 0.0f),
                new Vector3(-0.5f, 0.5f, 0.0f),
                new Vector3(0.5f, -0.5f, 0.0f),
                new Vector3(0.5f, 0.5f, 0.0f)
            };

            Indices = new uint[] { 0, 1, 2, 2, 3, 0 };
            Normals = new Vector3[] { 
                new Vector3(0.0f, 0.0f, 1.0f),
                new Vector3(0.0f, 0.0f, 1.0f),
                new Vector3(0.0f, 0.0f, 1.0f),
                new Vector3(0.0f, 0.0f, 1.0f)
            };

            TexCoord0 = new Vector2[] {
                new Vector2(0.0f, 1.0f), 
                new Vector2(0.0f, 0.0f), 
                new Vector2(1.0f, 1.0f), 
                new Vector2(1.0f, 0.0f)
            };
        }

        private Vector3 m_sphericalPosition = new Vector3(); //(r, theta, phi)
        public Vector3 SphericalPosition
        {
            get
            {
                return m_sphericalPosition;
            }

            set
            {
                //Convert Spherical coord to Cartesian coor
                m_sphericalPosition = value;
                double r = m_sphericalPosition.X;
                double theta = m_sphericalPosition.Y;
                double phi = m_sphericalPosition.Z;
                double x = r * Math.Cos(phi) * Math.Sin(theta);
                double y = r * Math.Sin(phi) * Math.Cos(theta);
                double z = r * Math.Cos(theta);
                
                //Transformation of plate (Billboard facing, spherical position)
                m_localTransform = Matrix.Translation((float)x, (float)y, (float)z); 
            }
        }

        Byte[] BitmapSource2ByteArray(BitmapSource source)
        {
            var encoder = new System.Windows.Media.Imaging.JpegBitmapEncoder();
            System.Windows.Media.Imaging.BitmapFrame frame = System.Windows.Media.Imaging.BitmapFrame.Create(source);
            encoder.Frames.Add(frame);
            var stream = new MemoryStream();
            encoder.Save(stream);
            return stream.ToArray();
        }

        static public void LoadTextureWithP(object obj)
        {
            System.Random rand = new Random();
            double d = rand.NextDouble();
            if (d > 0.05)
                return;
            LoadTexture(obj);
        }

        static public void LoadTexture(object obj)
        {
            ScreenPlate self = obj as ScreenPlate;
            var handle = new System.Runtime.InteropServices.HandleRef(null, (IntPtr)self.hwnd);
            var bmp = CaptureScreenshot.Capture(handle, out self.widthScaling);
            Texture2D texture = self.m_texture;

            if (texture == null)
            {
                Texture2DDescription desc = new Texture2DDescription()
                {
                    ArraySize = 1,
                    BindFlags = SlimDX.Direct3D10.BindFlags.ShaderResource,
                    CpuAccessFlags = SlimDX.Direct3D10.CpuAccessFlags.Write,
                    Format = SlimDX.DXGI.Format.B8G8R8A8_UNorm,
                    Height = (int)bmp.Height,
                    MipLevels = 1,
                    OptionFlags = ResourceOptionFlags.None,
                    SampleDescription = new SlimDX.DXGI.SampleDescription(1, 0),
                    Usage = ResourceUsage.Dynamic,
                    Width = (int)bmp.Width
                };
                texture = new Texture2D(self.m_scene.Renderer.m_device, desc);
                UpdateTexturePointer(obj, texture);
            }

            int bpp = (int)bmp.Format.BitsPerPixel / 8;
            byte[] bitmapData = new byte[texture.Description.Width * texture.Description.Height * bpp];
            int stride = (int)bmp.Width * bpp;
            bmp.CopyPixels(bitmapData, stride, 0);
            DataRectangle data = texture.Map(0, 0, MapMode.WriteDiscard, MapFlags.None);
            int pos = 0;
            int actualPitch = texture.Description.Width * bpp;
            int padSize = data.Pitch - actualPitch;
            if (padSize > 0)
            {
                byte[] pad = new byte[padSize];
                for (int i = 0; i < texture.Description.Height; ++i)
                {
                    data.Data.Write(bitmapData, pos, actualPitch);
                    data.Data.Write(pad, 0, padSize);
                    pos += actualPitch;
                }
            }
            else
            {
                data.Data.Write(bitmapData, 0, bitmapData.Length);
            }
            texture.Unmap(0);
        }

        static public void UpdateTexturePointer(object obj, Texture2D texture)
        {
            ScreenPlate self = obj as ScreenPlate;
            lock (self)
            {
                self.m_texture = texture;
            }
        }

        public override Texture2D GetTextureData()
        {
            if (m_texture != null)
            {
                //Update me
                //System.Threading.ThreadPool.QueueUserWorkItem(new System.Threading.WaitCallback(LoadTextureWithP), this);
                LoadTextureWithP(this);
                return m_texture;
            }
            else
            {
                LoadTexture(this);
                return m_texture;
            }
        }

        Texture2D m_texture;
        private float widthScaling;
        public float zoom;
        Matrix localTransform;
        public override Matrix GetLocalTransform()
        {
            Matrix matWidthScaling = Matrix.Scaling(widthScaling, 1.0f, 1.0f);
            matWidthScaling *= Matrix.Scaling(zoom, zoom, zoom);
            Matrix billboard = Matrix.RotationX(-m_scene.angleX) * Matrix.RotationY(-m_scene.angleY);
            localTransform = matWidthScaling * billboard * m_localTransform;
            return localTransform;
        }

        public override bool IntersectScreenSpace(float x, float y, out float d)
        {
            if (m_scene == null || m_scene.Renderer == null)
            {
                d = 0;
                return false;
            }
            Ray ray = m_scene.Renderer.GetRayFromScreenPos(x, y, WVP);
            return Ray.Intersects(ray, localBound, out d);
        }

        public IntPtr hwnd = (IntPtr) 0x10010;
        private BoundingBox localBound = new BoundingBox(new Vector3(-0.5f, -0.5f, -0.001f), new Vector3(0.5f, 0.5f, 0.001f));
        private Matrix m_localTransform;
    }
}
